package optional;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.util.*;

public class Demo {

    @Test
    public void testMyOwn() {
        Optional.<User>ofNullable(null).map(User::getCar).map(Car::getCarName).ifPresent(e -> {
            System.out.println("车名是：" + e);
        });
    }

    /**
     * optional 工具类的初始化方法
     * 1. 介绍三种构造方式
     * 2. 主要区别是初始化传入参数是否允许为null
     * Optional.of 不允许为空
     * Optional.ofNullable 允许为空
     * Optional.empty 构建空Optional对象
     */
    @Test
    public void testInit() {
        // 这种构造方式不能为null，否则会空指针异常
        Optional<Object> notNull = Optional.of(new Object());
        // 允许为空
        Optional<Object> nullAble = Optional.ofNullable(null);
        // 这种方式是返回一个空Optional，等效Optional.ofNullable(null)
        Optional<Object> empty = Optional.empty();
    }

    /**
     * optional 如何准确的获取对应的值
     * 1. Optional.map 使用map收集某一个对象的值,
     * 2. Optional.flatMap 根据 Optional 的结果获取参数
     * 插曲：map 和 flatMap 的区别
     * 3. Optional.filter 筛选出符合结果的参数
     */
    @Test
    public void testGetOptionalVal() {
        User user = new User();
        Car car = new Car("法拉利", "红色");
        user.setCar(car);
        Optional<User> userOptional = Optional.ofNullable(user);
        Integer age = userOptional.map(User::getAge).orElse(22);
        String name = userOptional.map(User::getName).orElse("myname");

        // Optional.map 收集某一个对象的值
        System.out.println("Optional.map 收集某一个对象的值：" + age);
        System.out.println("Optional.map 收集某一个对象的值：" + name);

        // Optional.flagMap 获取多层Optional迭代对象：
        Optional<String> s = userOptional.flatMap(User::getPersonCarName);
        Boolean aBoolean = s.map(String::trim).map(StringUtils::isNotBlank).get();
        System.out.println(aBoolean);

        // Optional.map 收集某一个对象的值
        User u1 = new User();
        u1.setName("小王");
        u1.setAge(11);
        User u2 = new User();
        List<User> userLists = new ArrayList<>();
        userLists.add(u1);
        userLists.add(u2);
        Optional<List<User>> notNull2 = Optional.of(userLists);
        // 针对对象集合，使用flagMap获取关联数据
        Optional<User> user1 = notNull2.flatMap(item -> Optional.ofNullable(item.get(0)));
        user1.ifPresent(u -> System.out.println("针对对象集合，使用flagMap获取关联数据 => " + u.getName()));

        // flatMap 的使用场景
        List<String> stringList = new ArrayList<>();
        stringList.add("name1");
        stringList.add("testone");
        stringList.add("other");
        // 使用flatMap处理Optional的返回结果
        Optional<String> stringOptional = Optional.of(u1).flatMap(u -> Optional.ofNullable(u1.getName()));
        System.out.println("flatMap" + stringOptional);

        // 对比：map和flatMap的区别
        // map 方法签名 Function<? super T, ? extends U> mapper
        Optional<String> map = userOptional.map(User::getName);
        // flatmap 的方法签名： Function<? super T, Optional<U>> mapper
        Optional<String> stringOptional1 = userOptional.flatMap(u -> Optional.ofNullable(u.getName()));
        // 虽然从结果来看两者的结果没有什么区别
        // map => Optional.empty
        //  flatMap => Optional.empty
        // 但是可以明显的看到
        // flatMap Function的返回值为 Optional 类型 Optional<String>
        // map 的 Function返回值为 具体返回类型 String + Optional 的自动封装 Option<String>
        System.out.println("map => " + map);
        System.out.println("flatMap => " + stringOptional1);

        // filter 方法使用
        Optional<String> optional = Optional.of("testNAME");
        String result = optional.filter(str -> str.contains("test")).orElse("not found");
        System.out.println(result);
    }

    /**
     * optional 校验对象属性等是否存在
     * 1. Optional.isPresent 校验对象是否存在，存在返回true
     * 2. Optional.orElse 如果为空返回默认值，不为空不做处理
     * 3. Optional.get 对象必须存在
     * 4. Optional.orElseGet 通过方法提供值
     * 5. Optional.orElseThrow 如果获取为null，抛出指定异常
     * 6. Optional.isPresent 使用ifPresent()来进行对象操作，存在则操作，否则不操作
     * 7. Optional.filter 操作，可以过滤出符合条件的内容
     */
    @Test
    public void testOptionalValExists() {
        // 对象属性是否存在
        Optional<Object> notNull = Optional.of(new Integer(4));
        boolean present = notNull.isPresent();
        System.out.println("notNull 值是否不为空 " + present);
        Optional<Object> nullAble = Optional.ofNullable("sss");
        System.out.println("nullAble 是否不为空 " + nullAble.isPresent());

        // Optional.orElse - 如果值存在，返回它，否则返回默认值
        Optional<Object> integerNull = Optional.ofNullable(null);
        Object o = integerNull.orElse("22");
        System.out.println("o 否则返回默认值 " + o);

        //Optional.get - 获取值，值需要存在
        Optional<Object> integerNull2 = Optional.ofNullable(null);
        // 抛出异常 java.common.NoSuchElementException: No value present
        // 来源：throw new NoSuchElementException("No value present");
        // Object o1 = integerNull2.get();
        Optional<Object> integerNull3 = Optional.ofNullable(12321);
        System.out.println("Optional.get 必须存在" + integerNull3.get());

        //通过方法提供值
        Optional<Object> integerNull4 = Optional.ofNullable(12321);
        Object o1 = integerNull4.orElseGet(() -> String.valueOf(22));
        System.out.println("Optional.orElseGet 通过方法提供值" + o1);

        // 如果获取为null，抛出指定异常
        Optional<Object> integerNull5 = Optional.ofNullable(null);
        // java.lang.RuntimeException: 当前运行代码有误 如果需要抛出异常，请放开下面的代码
        //        Object orElseThrow = integerNull5.orElseThrow(() -> new RuntimeException("当前运行代码有误"));
        //        System.out.println("Optional.orElseThrow 自定义异常" + orElseThrow);

        // Optional.isPresent 使用ifPresent()来进行对象操作，存在则操作，否则不操作
        integerNull5.ifPresent((item) -> {
            System.err.println("Optional.isPresent 如果存在对象，执行如下操作");
        });

        // filter 方法使用
        Optional<String> optional = Optional.of("testNAME");
        String result = optional.filter(str -> str.contains("test")).orElse("not found");
        System.out.println("Optional.filter 过滤出符合条件的对象: " + result);
    }

    /**
     * 实际使用场景
     * 1. 我们要将一个对象的名称全部统一为大写，防止空指针. 但是实际使用来看还是遇到了不少的问题
     */
    @Test
    public void actualUse() {
        User user = new User();
        // java.lang.NullPointerException 如果编程习惯不好，这种工具类其实并不能解决问题
        //        Optional.ofNullable(user).ifPresent(u->{
        //            String toLowerCase = u.getName().toLowerCase();
        //            u.setName(toLowerCase);
        //        });
        // 正确的使用方式应该是如下的形式：
        // 下面的语句放开注释运行打印结果为： SSS
        //        user.setName("sss");
        // 如果为null会抛出 java.lang.RuntimeException
        //        String s = Optional.ofNullable(user).map(User::getName).map(String::toUpperCase).orElseThrow(RuntimeException::new);
        //        System.out.println(s);
        // 我们也可以用另一种方式
        String s2 = Optional.ofNullable(user).map(User::getName).map(String::toUpperCase).orElse("默认值");
        System.out.println(s2);

        // 我们要对一个前端传入的值进行split或者substring的时候
        // 案例数据除开分隔符有差异之外无任何差异
        String tags1 = "标签1,标签2,标签3,标签4";
        String tags2 = "标签1，标签2，标签3，标签4";
        String[] strings1 = Optional.of(tags1).map(tg -> tags1.split(",")).get();
        System.out.println(strings1[0]);
        String[] strings2 = Optional.of(tags2).map(tg -> tags2.split(",")).get();
        String[] strings3 = Optional.ofNullable(tags2).map(StringUtils::isNotBlank).map(tg -> tags2.split(",")).orElse(new String[]{"ss"});
        System.out.println(strings2[0]);
        System.out.println(Arrays.toString(strings3));
        // 运行结果，如果此时有值，基本无问题
        // 标签1
        // 标签1，标签2，标签3，标签4

        // 如果上面的案例当中，传入的为null会如何?
        // 所以我们需要修改上面的格式，确保不管tag的值是否存在，都可以只关心我们具体需要操作的数据
        // 如果为null，则没有任何结果处理。我们可以使用map进行各种操作
        //String tags3 = "null,222";
        String tags3 = "a,b|C|d";
        Optional.ofNullable(tags3).map(tg -> tags3.split(",")).map(tg -> {
            for (int i = 0; i < tg.length; i++) {
                tg[i] = tg[i].toUpperCase();
            }
            return tg;
        }).ifPresent(item -> {
            for (String s : item) {
                System.out.println(s);
            }
        });
    }

}
